home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
JFC.bin
/
LargeTreeModelNode.java
< prev
next >
Wrap
Text File
|
1998-06-30
|
16KB
|
520 lines
/*
* @(#)LargeTreeModelNode.java 1.7 98/02/02
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.plaf.basic;
import com.sun.java.swing.tree.DefaultMutableTreeNode;
import com.sun.java.swing.tree.TreePath;
import com.sun.java.swing.tree.TreeModel;
import com.sun.java.swing.tree.TreeSelectionModel;
/**
* LargeTreeModelNode is used by AbstractTreeUI to track what has been
* expanded. LargeTreeModelNode differs from VisibleTreeNode in that it
* is highly model intensive. That is almost all queries to a
* LargeTreeModelNode result in the TreeModel being queried. It also
* will not support odd sized row heights.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @version 1.7 02/02/98
* @author Scott Violet
*/
public class LargeTreeModelNode extends DefaultMutableTreeNode {
/** Tree UI this is created for. */
protected AbstractTreeUI treeUI;
/** Is this node expanded? */
protected boolean isExpanded;
/** Index of this node from the model. */
protected int childIndex;
public LargeTreeModelNode(AbstractTreeUI treeUI, Object userObject,
int childIndex) {
super(userObject);
this.treeUI = treeUI;
this.childIndex = childIndex;
}
/**
* Returns the model for the current tree.
*/
public TreeModel getModel() {
return treeUI.getModel();
}
/**
* Returns the index of the reciever in the model.
*/
public int getChildIndex() {
return childIndex;
}
/**
* Returns the child for the passed in model index.
*/
public LargeTreeModelNode childAtModelIndex(int index) {
for(int counter = getChildCount() - 1; counter >= 0; counter--)
if(((LargeTreeModelNode)getChildAt(counter)).childIndex == index)
return (LargeTreeModelNode)getChildAt(counter);
return null;
}
/**
* Returns true if this node is visible. This is determined by
* asking all the parents if they are expanded.
*/
public boolean isVisible() {
LargeTreeModelNode parent = (LargeTreeModelNode)getParent();
if(parent == null)
return true;
return (parent.isExpanded() && parent.isVisible());
}
/**
* Returns the row of the receiver.
*/
public int getRow() {
LargeTreeModelNode parent = (LargeTreeModelNode)getParent();
if(parent == null) {
if(treeUI.isRootVisible())
return 0;
return -1;
}
return parent.getCountTo(childIndex);
}
/**
* Expands the receiver. If adjustTree is true didAdjustTree and
* visibleNodesChanged is messaged.
*/
public void expand(boolean adjustTree) {
if(!isExpanded) {
boolean visible = isVisible();
isExpanded = true;
if(visible)
adjustLargeRowCountBy(getTotalChildCount());
if(adjustTree) {
didAdjustTree();
treeUI.visibleNodesChanged();
}
treeUI.pathWasExpanded(new TreePath(getUserObjectPath()));
/* Update the selection model, and the selected entries if
the receivers row and the row after it are selected. */
if(treeUI != null && visible) {
int cCount;
int row = getRow();
TreeSelectionModel selModel = treeUI.getSelectionModel();
if(selModel != null && (cCount = getTotalChildCount()) > 0 &&
selModel.isRowSelected(row) &&
selModel.isRowSelected(row + 1)) {
TreePath[] paths = new TreePath[cCount];
for(int counter = 0; counter < cCount; counter++)
paths[counter] = treeUI.getPathForRow(counter + row+1);
selModel.addSelectionPaths(paths);
}
else if(selModel != null)
selModel.resetRowSelection();
}
}
}
/**
* Collapses the receiver. If <code>adjustTree</code> is true
* didAdjustTree and pathWasCollapsed are messaged.
*/
public void collapse(boolean adjustTree) {
if(isExpanded) {
TreePath[] selPaths;
TreeSelectionModel selModel = null;
if(treeUI != null && (selModel = treeUI.getSelectionModel())
!= null)
selPaths = selModel.getSelectionPaths();
else
selPaths = null;
if(isVisible())
adjustLargeRowCountBy(-getTotalChildCount());
isExpanded = false;
if(adjustTree) {
didAdjustTree();
treeUI.visibleNodesChanged();
}
treeUI.pathWasCollapsed(new TreePath(getUserObjectPath()));
/* update the selection */
if(selPaths != null) {
boolean shouldRemove = false;
TreePath ourPath = new TreePath(getUserObjectPath());
for(int counter = selPaths.length - 1; counter >= 0;
counter--) {
if(selPaths[counter] != null &&
ourPath.isDescendant(selPaths[counter]) &&
!ourPath.equals(selPaths[counter]))
shouldRemove = true;
else
selPaths[counter] = null;
}
if(shouldRemove)
selModel.removeSelectionPaths(selPaths);
}
}
}
/**
* Returns the number of children in the receiver by descending all
* expanded nodes and messaging them with getTotalChildCount.
*/
public int getTotalChildCount() {
if(isExpanded()) {
int retCount = getModel().getChildCount(getUserObject());
for(int counter = getChildCount() - 1; counter >= 0;
counter--) {
retCount += ((LargeTreeModelNode)getChildAt(counter))
.getTotalChildCount();
}
return retCount;
}
return 0;
}
/**
* Returns true if this node is expanded.
*/
public boolean isExpanded() {
return isExpanded;
}
/**
* Messaged when the child count has changed and this node hasn't
* yet been expanded, does nothing.
*/
public void modelChildCountChanged() {
}
/**
* The highest visible nodes have a depth of 0.
*/
public int getVisibleLevel() {
if (treeUI.isRootVisible()) {
return getLevel();
} else {
return getLevel()-1;
}
}
/**
* Makes the receiver collapse if it is currently expanded, otherwise
* expands the reciever.
*/
public void toggleExpanded() {
if (isExpanded()) {
collapse(true);
} else {
expand(true);
}
}
/**
* Adjust the large row count of the AbstractTreeUI the receiver was
* created with.
*/
protected void adjustLargeRowCountBy(int changeAmount) {
treeUI.largeRowCount += changeAmount;
}
/**
* Adds newChild to this nodes children at the appropriate location.
* The location is determined from the childIndex of newChild.
*/
protected void addLargeTreeModelNode(LargeTreeModelNode newChild) {
boolean added = false;
int childIndex = newChild.getChildIndex();
for(int counter = 0, maxCounter = getChildCount();
counter < maxCounter; counter++) {
if(((LargeTreeModelNode)getChildAt(counter)).getChildIndex() >
childIndex) {
added = true;
insert(newChild, counter);
counter = maxCounter;
}
}
if(!added)
add(newChild);
}
/**
* Messaged when a child has been removed from the model at the
* specified index. Will shift down all the childIndexs that
* are >= index.
*/
protected void childRemovedAtModelIndex(int index) {
LargeTreeModelNode aChild;
for(int counter = 0, maxCounter = getChildCount();
counter < maxCounter; counter++) {
aChild = (LargeTreeModelNode)getChildAt(counter);
if(aChild.childIndex >= index) {
/* Since matched and children are always sorted by
index, no need to continue testing with the above. */
for(; counter < maxCounter; counter++)
((LargeTreeModelNode)getChildAt(counter)).childIndex--;
}
}
}
/**
* Messaged when a child has been inserted at index. For all the
* children that have a childIndex >= index their index is incremented
* by one.
*/
protected void childInsertedAtModelIndex(int index) {
LargeTreeModelNode aChild;
for(int counter = 0, maxCounter = getChildCount();
counter < maxCounter; counter++) {
aChild = (LargeTreeModelNode)getChildAt(counter);
if(aChild.childIndex >= index) {
/* Since matched and children are always sorted by
index, no need to continue testing with the above. */
for(; counter < maxCounter; counter++)
((LargeTreeModelNode)getChildAt(counter)).childIndex++;
}
}
}
/**
* Returns the TreePath for the given row. This will return null
* if the row is greater than the number of expanded nodes.<p>
* rowCounter is used to count the number of nodes while descending
* the hierarchy. <p>
* If eNode is non-null then eNode will be set to either the matching
* node, or its parent if the last element has not yet been
* expanded, or is a leaf.<p>
* isParentNode will be set to true if the parent is set in
* eNode, otherwise false.<p>
* The TreePath will be returned in retPath (if it is non-null)
* Returns the child index of the returned row in childIndex (if
* non-null).<p>
* The reason for all these arguments is different methods call
* this with different requirements and rather than having 4
* different methods, there is one big one.<p>
* If no match is found, false is returned, otherwise true.
*/
protected boolean getPathForRow(int row, int[] rowCounter,
TreePath[] retPath,
LargeTreeModelNode[] eNode,
boolean[] isParentNode,
int[] childIndex) {
if(row == rowCounter[0]) {
if(childIndex != null)
childIndex[0] = getChildIndex();
if(eNode != null) {
eNode[0] = this;
isParentNode[0] = false;
}
if(retPath != null)
retPath[0] = new TreePath(getUserObjectPath());
return true;
}
rowCounter[0]++;
if(isExpanded) {
LargeTreeModelNode aNode;
int endIndex;
int lastChildIndex = 0;
int newChildIndex;
TreeModel treeModel = getModel();
for(int counter = 0, maxCounter = getChildCount();
counter < maxCounter; counter++) {
aNode = (LargeTreeModelNode)getChildAt(counter);
newChildIndex = aNode.childIndex;
if((rowCounter[0] + (newChildIndex - lastChildIndex)) >
row) {
if(childIndex != null)
childIndex[0] = row - rowCounter[0] +
lastChildIndex;
if(retPath != null) {
Object child;
Object[] thisPath = getUserObjectPath();
int pLength = thisPath.length;
Object[] newPath = new Object[pLength + 1];
child = treeModel.getChild(userObject,
(row - rowCounter[0] + lastChildIndex));
System.arraycopy(thisPath, 0, newPath, 0, pLength);
newPath[pLength] = child;
retPath[0] = new TreePath(newPath);
}
if(eNode != null) {
eNode[0] = this;
isParentNode[0] = true;
}
return true;
}
rowCounter[0] += (newChildIndex - lastChildIndex);
lastChildIndex = newChildIndex + 1;
if(aNode.getPathForRow(row, rowCounter, retPath, eNode,
isParentNode, childIndex)) {
return true;
}
}
newChildIndex = treeModel.getChildCount(userObject) - 1;
if((newChildIndex - lastChildIndex) >= 0) {
if((rowCounter[0] + (newChildIndex - lastChildIndex))
>= row) {
Object child;
if(childIndex != null)
childIndex[0] = row - rowCounter[0] +
lastChildIndex;
if(retPath != null) {
Object[] thisPath = getUserObjectPath();
int pLength = thisPath.length;
Object[] newPath = new Object[pLength + 1];
child = treeModel.getChild(userObject,
(row - rowCounter[0] + lastChildIndex));
System.arraycopy(thisPath, 0, newPath, 0, pLength);
newPath[pLength] = child;
retPath[0] = new TreePath(newPath);
}
if(eNode != null) {
eNode[0] = this;
isParentNode[0] = true;
}
return true;
}
rowCounter[0] += (newChildIndex - lastChildIndex) + 1;
}
}
return false;
}
/**
* Asks all the children of the receiver for their totalChildCount
* and returns this value (plus stopIndex).
*/
protected int getCountTo(int stopIndex) {
LargeTreeModelNode aChild;
int retCount = stopIndex + 1;
for(int counter = 0, maxCounter = getChildCount();
counter < maxCounter; counter++) {
aChild = (LargeTreeModelNode)getChildAt(counter);
if(aChild.childIndex >= stopIndex)
counter = maxCounter;
else
retCount += aChild.getTotalChildCount();
}
if(parent != null)
return retCount + ((LargeTreeModelNode)getParent())
.getCountTo(childIndex);
if(!treeUI.isRootVisible())
return (retCount - 1);
return retCount;
}
/**
* Returns, by reference in rowCounter, the row for the given
* path. This is meant to be called from the root, it will not
* compute the row of the receiver.<p>
* Path is the path that is being searched for.
* pathCounter is the current index into path
* pathLength is the length of the path (avoids path.length);
* isInPath is true if the parents path is contained in path.
* returns true if the row is found.
*/
protected boolean getRow(Object[] path, int pathCounter,
int pathLength, boolean isInPath,
int[] rowCounter) {
isInPath = (isInPath && path[pathCounter].equals(userObject));
if(isInPath) {
if(++pathCounter == pathLength)
return true;
}
rowCounter[0]++;
if(isExpanded) {
LargeTreeModelNode aNode;
int endIndex;
int lastChildIndex;
int newChildIndex;
int newRowCount;
TreeModel treeModel = getModel();
if(isInPath && (pathCounter + 1) == pathLength)
endIndex = treeModel.getIndexOfChild(userObject,
path[pathLength - 1]);
else
endIndex = Integer.MAX_VALUE;
lastChildIndex = 0;
newRowCount = rowCounter[0];
for(int counter = 0, maxCounter = getChildCount();
counter < maxCounter; counter++) {
aNode = (LargeTreeModelNode)getChildAt(counter);
newChildIndex = aNode.childIndex;
if(newChildIndex >= endIndex) {
rowCounter[0] = newRowCount +
(endIndex - lastChildIndex);
return true;
}
newRowCount += (newChildIndex - lastChildIndex);
lastChildIndex = newChildIndex + 1;
rowCounter[0] = newRowCount;
if(aNode.getRow(path, pathCounter, pathLength,
isInPath, rowCounter))
return true;
newRowCount = rowCounter[0];
}
newChildIndex = treeModel.getChildCount(userObject) - 1;
if(newChildIndex >= 0) {
if(newChildIndex >= endIndex) {
rowCounter[0] = newRowCount +
(endIndex - lastChildIndex);
return true;
}
rowCounter[0] += (newChildIndex - lastChildIndex) + 1;
}
}
return false;
}
/**
* Messaged when this node either expands or collapses.
*/
protected void didAdjustTree() {
}
}